"======================================================================
"
" highlight.vim -
"
" Created by skywind on 2021/12/12
" Last Modified: 2021/12/13 18:32
"
"======================================================================

" vim: set ts=4 sw=4 tw=78 noet :


"----------------------------------------------------------------------
" internal
"----------------------------------------------------------------------
let s:has_hlget = exists('*hlget')? 1 : 0
let s:has_hlset = exists('*hlset')? 1 : 0


"----------------------------------------------------------------------
" get highlighting group
"----------------------------------------------------------------------
function! s:sim_hlget(name)
    let error = 0
    redir => g:quickui_highlight_tmp
    try
        exec 'silent hi ' . a:name
    catch
        let error = 1
    endtry
    redir END
    if error != 0
        return []
    endif
    let capture = g:quickui_highlight_tmp
    let items = []
    for text in split(capture, '\n')
        let text = quickui#core#string_strip(text)
        if text == ''
            continue
        endif
        let item = {}
        let item.name = matchstr(text, '^\w\+')
        if item.name == ''
            continue
        endif
        let parts = split(text, ' ')
        if empty(parts)
            continue
        endif
        if text =~ ' cleared$'
            let item.cleared = v:true
        elseif text =~ ' links to \w\+$'
            let links = matchstr(text, ' links to \zs\w\+$')
            let item.linksto = quickui#core#string_strip(links)
        else
            for part in parts[1:]
                if part =~ '\w\+='
                    let key = matchstr(part, '^\w\+')
                    let val = matchstr(part, '^\w\+=\zs\%(\\.\|\S\)*')
                    if key == 'term' || key == 'cterm' || key == 'gui'
                        let opts = {}
                        for element in split(val, ',')
                            let opts[element] = v:true
                        endfor
                        let item[key] = opts
                    else
                        let item[key] = val
                    endif
                elseif part == 'cleared'
                endif
            endfor
        endif
        let items += [item]
    endfor
    return items
endfunc


"----------------------------------------------------------------------
" simulate hlset
"----------------------------------------------------------------------
function! s:sim_hlset(items)
    let skip = {'name':1, 'id':1, 'linksto':1, 'force':1}
    for item in a:items
        let name = get(item, 'name', '')
        if name == ''
            continue
        endif
        let force = get(item, 'force', v:false)
        let cmd = (force == 0)? 'hi ' : 'hi! '
        if get(item, 'cleared', v:false) == v:true
            exec cmd . 'clear ' . name
        else
            let part = []
            for key in keys(item)
                if has_key(skip, key) == 0
                    let val = item[key]
                    if type(val) == v:t_dict
                        let r = join(keys(val), ',')
                    else
                        let r = val
                    endif
                    let part += [key . '=' . r]
                endif
            endfor
            let text = cmd . ' ' . name . ' ' . join(part, ' ')
            exec text
        endif
    endfor
endfunc


"----------------------------------------------------------------------
" get highlighting info
"----------------------------------------------------------------------
function! quickui#highlight#get(name, ...)
    let resolve = (a:0 > 0)? (a:1) : 0
    if s:has_hlget
        " return hlget(a:name, resolve)
    endif
    if !resolve
        return s:sim_hlget(a:name)
    endif
    let items = []
    for item in s:sim_hlget(a:name)
        if has_key(item, 'linksto') == 0
            let items += [item]
            continue
        endif
        let info = item
        while 1
            if has_key(info, 'linksto') == 0
                break
            endif
            let links = info.linksto
            let hr = s:sim_hlget(links)
            if empty(hr)
                break
            endif
            let info = hr[0]
        endwhile
        let info.name = item.name
        let items += [info]
    endfor
    return items
endfunc


"----------------------------------------------------------------------
" set highlight group
"----------------------------------------------------------------------
function! quickui#highlight#set(items)
    if s:has_hlset
        " return hlset(a:items)
    endif
    call s:sim_hlset(a:items)
endfunc


"----------------------------------------------------------------------
" clear highlight
"----------------------------------------------------------------------
function! quickui#highlight#clear(name)
    if s:has_hlset
        let info = {'name': a:name, 'cleared': v:true}
        call hlset([info])
    else
        exec 'hi! clear ' . a:name
    endif
endfunc


"----------------------------------------------------------------------
" term add feature
"----------------------------------------------------------------------
function! quickui#highlight#term_add(info, what)
    let info = a:info
    let what = a:what
    if has_key(info, 'term')
        if type(info.term) == v:t_dict
            let info.term[what] = v:true
        elseif type(info.term) == v:t_string
            let opts = {}
            for key in split(info.term, ',')
                let opts[key] = v:true
            endfor
            let opts[what] = v:true
            let info.term = opts
        endif
    else
        let info.term = {}
        let info.term[what] = v:true
    endif
endfunc


"----------------------------------------------------------------------
" cterm add feature
"----------------------------------------------------------------------
function! quickui#highlight#cterm_add(info, what)
    let info = a:info
    let what = a:what
    if has_key(info, 'cterm')
        if type(info.cterm) == v:t_dict
            let info.cterm[what] = v:true
        elseif type(info.cterm) == v:t_string
            let opts = {}
            for key in split(info.cterm, ',')
                let opts[key] = v:true
            endfor
            let opts[what] = v:true
            let info.cterm = opts
        endif
    else
        let info.cterm = {}
        let info.cterm[what] = v:true
    endif
endfunc


"----------------------------------------------------------------------
" gui add feature
"----------------------------------------------------------------------
function! quickui#highlight#gui_add(info, what)
    let info = a:info
    let what = a:what
    if has_key(info, 'gui')
        if type(info.gui) == v:t_dict
            let info.gui[what] = v:true
        elseif type(info.gui) == v:t_string
            let opts = {}
            for key in split(info.gui, ',')
                let opts[key] = v:true
            endfor
            let opts[what] = v:true
            let info.gui = opts
        endif
    else
        let info.gui = {}
        let info.gui[what] = v:true
    endif
endfunc


"----------------------------------------------------------------------
" new underline
"----------------------------------------------------------------------
function! quickui#highlight#grant_underline(info)
    let info = a:info
    call quickui#highlight#term_add(info, 'underline')
    call quickui#highlight#cterm_add(info, 'underline')
    call quickui#highlight#gui_add(info, 'underline')
    return info
endfunc


"----------------------------------------------------------------------
" add colors
"----------------------------------------------------------------------
function! quickui#highlight#grant_color(info, colors)
    for key in keys(a:colors)
        let a:info[key] = a:colors[key]
    endfor
    return a:info
endfunc


"----------------------------------------------------------------------
" add underline feature
"----------------------------------------------------------------------
function! quickui#highlight#make_underline(newname, name)
    let hr = quickui#highlight#get(a:name, 1)
    if len(hr) == 0
        return -1
    endif
    let info = (len(hr) == 0)? {} : hr[0]
    call quickui#highlight#term_add(info, 'underline')
    call quickui#highlight#cterm_add(info, 'underline')
    call quickui#highlight#gui_add(info, 'underline')
    if has_key(info, 'id')
        unlet info['id']
    endif
    let info.name = a:newname
    let info.force = v:true
    call quickui#highlight#set([info])
    return info
endfunc



"----------------------------------------------------------------------
" combine foreground and background colors
"----------------------------------------------------------------------
function! quickui#highlight#overlay(newname, background, foreground)
    let hr1 = quickui#highlight#get(a:background, 1)
    let hr2 = quickui#highlight#get(a:foreground, 1)
    let info1 = empty(hr1)? {} : hr1[0]
    let info2 = empty(hr2)? {} : hr2[0]
    for key in ['ctermfg', 'guifg']
        if has_key(info2, key)
            let info1[key] = info2[key]
        endif
    endfor
    let info1.name = a:newname
    let info1.force = v:true
    call quickui#highlight#set([info1])
endfunc


